<template>
  <div>
    <el-tabs type="card" class="demo-tabs" v-model="activeIndex" @tab-change="getData">
      <el-tab-pane v-for="account in accountList" :key="`account-tab-list-${account.id}`" :name="account.id">
        <template #label>
          <icon-platform :name="getCodeByType(account.type)" style="color: #0077aa" />
          &nbsp;
          {{account.name}}
        </template>
      </el-tab-pane>
    </el-tabs>
    <el-row style="margin-top: 10px">
      <el-table border :data="tableData" style="width: 100%">
        <el-table-column prop="domain" label="域名" :show-overflow-tooltip="true" />
        <el-table-column prop="expire_date" label="SSL证书到期时间" :show-overflow-tooltip="true">
          <template #default="scope">
            <span v-html="formatDateLevel(scope.row.expire_date)"></span>
          </template>
        </el-table-column>
        <el-table-column prop="provider" label="SSL证书类型" :show-overflow-tooltip="true" />
        <el-table-column label="操作" width="50">
          <template #default="scope">
            <el-dropdown>
              <span class="el-dropdown-link">
                <el-icon class="el-icon--right">
                  <more-filled />
                </el-icon>
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item @click="deleteRow(scope.row)"><el-icon><Delete /></el-icon> 删除</el-dropdown-item>
                  <el-dropdown-item @click="showService(scope.row)"><i class="iconfont icon-weiwangguanicon-defuben-" style="font-size: 12px"></i> 关联服务器</el-dropdown-item>
                  <el-dropdown-item @click="downCert(scope.row)"><el-icon><Download /></el-icon> 下载</el-dropdown-item>
                  <el-dropdown-item @click="startDeploy(scope.row)"><i class="iconfont icon-liuchengbushu" style="font-size: 12px"></i> 自动部署</el-dropdown-item>
                  <el-dropdown-item @click="renewal(scope.row)"><el-icon><Clock /></el-icon> 续期</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </template>
        </el-table-column>
      </el-table>

      <div class="page-block" >
        <el-pagination small layout="prev, pager, next" :page-size="page.size" :total="page.total" :current-change="getData" />
      </div>
    </el-row>

    <domain-service ref="refDomainService" @refresh="getData(true)" />
    <deploy-terminal ref="refDeployTerminal" />
  </div>
</template>

<script>
import { Delete, MoreFilled, Download, Clock } from '@element-plus/icons-vue'
import StoreCloudAccount from "@/store/StoreCloudAccount"
import IconPlatform from "@/components/common/IconPlatform.vue"
import EnumPlatform from "@/config/EnumPlatform"
import ApiTencentSSL from "@/api/ApiTencentSSL"
import MessageHandler from "@/utils/MessagerHandler"
import SSLColumnFormat from "@/config/SSLColumnFormat"
import TimeUtils from "@/utils/TimeUtils"
import DomainService from "@/components/domain/DomainService.vue"
import DeployTerminal from "@/components/domain/DeployTerminal.vue"
import StoreSSLServer from "@/store/StoreSSLServer";
import StoreServer from "@/store/StoreServer";
import StoreProof from "@/store/StoreProof";
import JSZip from 'jszip'
import EnumSSLUseType from "@/config/EnumSSLUseType";
import StringUtils from "@/utils/StringUtils";
const fs = require('fs')
const { dialog, shell, app } = require('electron').remote
const SSHClient = require('ssh2').Client

export default {
  name: 'DomainManage',
  components: {
    DomainService, DeployTerminal, IconPlatform,
    Delete, MoreFilled, Download, Clock
  },
  data () {
    return {
      activeIndex: 0,
      accountList: [],
      tableData: [],
      page: {
        size: 20,
        page: 1,
        total: 1
      },

      deployInfo: {}
    }
  },
  methods: {
    /**
     * 加载账号.
     */
    loadAccount() {
      StoreCloudAccount.getAll({}, (data) => {
        console.log('account data: ', data)
        this.accountList = data
        if (this.accountList.length > 0) {
          this.activeIndex = this.accountList[0].id
          this.getData(true)
        }
      })
    },
    /**
     * 根据类型获取平台编码.
     * @param type
     * @returns {*|string}
     */
    getCodeByType(type) {
      const platform = EnumPlatform.getByType(type)
      return platform ? platform.code : ''
    },
    /**
     * 加载数据.
     */
    getData(resetPage) {
      if (resetPage) {
        this.page.page = 1
      }
      this.tableData = []
      const account = this.getAccount()
      console.log('account: ', account)
      if (account.type === EnumPlatform.Tencent.type) {
        ApiTencentSSL.getSSLList(account, {
          Offset: (this.page.page - 1) * this.page.size,
          Limit: this.page.size
        }, (data) => {
          console.log('ssl data: ', data)
          const sslList = data.Certificates
          this.tableData = SSLColumnFormat.formatList(sslList, SSLColumnFormat.TencentToStandardMap)
          this.page.total = data.TotalCount
        }, error => {
          MessageHandler.error(error)
        })
      }
    },
    /**
     * 获取账号.
     */
    getAccount() {
      let findAccount = null
      this.accountList.forEach(account => {
        if (account.id === parseInt(this.activeIndex)) {
          findAccount = Object.assign({}, account)
        }
      })
      return findAccount
    },
    /**
     * 格式化日期级别.
     * @param dateStr {string}
     * @returns {*|string}
     */
    formatDateLevel(dateStr) {
      if (null == dateStr) {
        return ''
      }
      const expireDate = new Date(dateStr)
      let differDays = TimeUtils.daysBetween(expireDate, new Date());
      if (differDays < 0) {
        return `<span style="color:#F56C6C">${dateStr}</span>`
      } else if (differDays < 15) {
        return `<span style="color:#E6A23C">${dateStr}</span>`
      }
      return dateStr
    },
    /**
     * 删除.
     */
    deleteRow(row) {
      this.$confirm('确定需要删除该SSL域名吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        if (this.getAccount().type === EnumPlatform.Tencent.type) {
          ApiTencentSSL.del(row.id, this.getAccount(), () => {
            this.$message.success('删除成功')
            this.getData()
          }, error => {
            MessageHandler.error(error)
          })
        }
      })
    },
    /**
     * 下载证书.
     * @param row {Object}
     */
    downCert(row) {
      if (this.getAccount().type === EnumPlatform.Tencent.type) {
        ApiTencentSSL.down(row.id, this.getAccount(), (data) => {
          let options = {
            title: '下载证书',
            defaultPath: `${row.domain}.zip`
          }
          dialog.showSaveDialog(options).then((files) => {
            if (files) {
              try {
                fs.writeFileSync(files.filePath, new Buffer(data.Content, 'base64'));
                shell.showItemInFolder(files.filePath);
              } catch (e) {
                console.log('save file error: ', e)
              }
            }
          }).catch((req)=>{
            console.log('save exception', req);
          }).finally(() => {

          });
        }, error => {
          MessageHandler.error(error)
        })
      }
    },
    /**
     * 显示关联服务器窗口.
     * @param row {Object} SSL实体.
     */
    showService(row) {
      this.$refs.refDomainService.show(Object.assign({
        cloud_type: this.activeIndex
      }, row))
    },
    /**
     * 自动部署.
     */
    startDeploy(row) {
      StoreSSLServer.getAll({
        cloud_type: this.activeIndex,
        ssl_id: row.id
      }, (serverList) => {
        serverList.forEach((item) => {
          StoreServer.getOne({
            id: item.server_id
          }, (server) => {
            StoreProof.getOne({
              id: server.proof_id
            }, (proof) => {
              this.$refs.refDeployTerminal.clear()
              this.$refs.refDeployTerminal.show(`部署到 ${server.address} `)
              this.deployInfo = {}
              this._doSSHBiz(row, item, server, proof)

            })
          })
        })
      })
    },

    /**
     * 下载并解压SSL证书到临时目录.
     * @param row {Object}
     * @param needFiles {Array} 需要下载的文件列表.
     * @param callback {Function} 回调函数.
     * @private
     */
    _downFileToTemp(row, needFiles, callback) {
      const matchFiles = []
      if (this.getAccount().type === EnumPlatform.Tencent.type) {
        ApiTencentSSL.down(row.id, this.getAccount(), (data) => {
          const savePath = app.getPath('temp')
          console.log('savePath: ', savePath)
          const saveFilePath = `${savePath}/${row.domain}.zip`
          const unzipPath =  `${savePath}/${row.domain}`
          if (!fs.existsSync(unzipPath)) {
            fs.mkdirSync(unzipPath)
          }
          try {
            fs.writeFileSync(saveFilePath, new Buffer(data.Content, 'base64'))
            const zip = new JSZip()
            let executeCallback = false
            zip.loadAsync(fs.readFileSync(saveFilePath)).then((zipContent) => {
              Object.keys(zipContent.files).forEach((fileName) => {
                if (fileName.endsWith('/')) {
                  const dirPath = `${unzipPath}/${fileName}`
                  if (!fs.existsSync(dirPath)) {
                    fs.mkdirSync(dirPath)
                  }
                } else {
                  zip.file(fileName).async('nodebuffer').then((data) => {
                    const filePath = `${unzipPath}/${fileName}`
                    if (!this.deployInfo.ssl_files) {
                      this.deployInfo.ssl_files = []
                    }
                    this.deployInfo.ssl_files.push({
                      file_name: fileName,
                      file_path: filePath
                    })
                    fs.writeFileSync(filePath, data)
                    if (needFiles.includes(fileName)) {
                      matchFiles.push(fileName)
                    }
                    console.log('filePath: ', filePath, fileName, needFiles, matchFiles)
                    if (matchFiles.length === needFiles.length && !executeCallback) {
                      executeCallback = true
                      callback()
                    }
                  })
                }
              })
            })

          } catch (e) {
            console.log('save file error: ', e)
          }
        })
      }
    },

    /**
     * 获取需要下载的文件列表.
     * @param row {Object}
     * @param sslServer {Object}
     * @returns {{need: *[], upload: {}}}
     * @private
     */
    _getNeedSSLFiles(row, sslServer) {
      const updateFiles = JSON.parse(sslServer.update_files)
      const needFiles = []
      const uploadFiles = {}
      console.log('updateFiles: ', updateFiles)
      updateFiles.forEach((item) => {
        if (EnumPlatform.Tencent.type === this.activeIndex) {
          // 腾讯云证书文件名.
          const zipDir = EnumSSLUseType.getByValue(item.use_type).dir.Tencent
          item.file_type_list.forEach((fileType) => {
            // todo: 暂时只支持nginx部署
            if (item[`file_${fileType}`]) {
              let filename
              if (fileType === 'pem') {
                filename = `${row.domain}.${fileType}`
              } else if (fileType === 'crt') {
                filename = `${zipDir}1_${row.domain}_bundle.${fileType}`
              } else if (fileType === 'key') {
                filename = `${zipDir}2_${row.domain}.${fileType}`
              } else {
                filename = `${zipDir}${row.domain}.${fileType}`
              }
              needFiles.push(filename)
              uploadFiles[filename] = item[`file_${fileType}`]
            }
          })
        }
      })
      return {need: needFiles, upload: uploadFiles}
    },

    /**
     * 查找证书保存路径.
     * @param filename
     */
    _findSavePath(filename) {
      let savePath = ''
      this.deployInfo.ssl_files.forEach((item) => {
        if (item.file_name === filename) {
          savePath = item.file_path
        }
      })
      return savePath
    },

    /**
     * SSH部署业务逻辑.
     * @param row {Object}
     * @param sslServer {Object}
     * @param server {Object}
     * @param proof {Object}
     * @private
     */
    _doSSHBiz(row, sslServer, server, proof) {
      console.log('doSSHBiz: ', row, sslServer, server, proof)
      //计算需要的证书文件列表.
      const needFiles = this._getNeedSSLFiles(row, sslServer)
      console.log('needFiles: ', needFiles)

      // 先下载SSL证书到临时目录.
      this._downFileToTemp(row, needFiles.need, () => {
        // 上传SSL证书到服务器.
        let sshClient = new SSHClient()

        sshClient.on('ready', () => {
          this.$refs.refDeployTerminal.append('Connection::ready\n')

          // 执行后置命令
          const afterFunction = () => {
            if (!StringUtils.isBlank(sslServer.after_command)) {
              sshClient.shell({
              }, (err, stream) => {
                if (err) {
                  this.$refs.refDeployTerminal.append('Connection::fail')
                  console.log('fail: ', err)
                  throw err
                }

                stream.setEncoding('utf8')

                let dealFun = null

                stream.on('close', (code, signal) => {
                  this.$refs.refDeployTerminal.append('Connection::closed')
                  console.log('code:', code, 'signal:', signal)
                  sshClient.end()
                }).on('data', result => {
                  console.log('result:', result, result.toString('utf8'), result.toString('utf16le'),
                      result.toString('latin1'), result.toString('ascii'))
                  //let str = Iconv.decode(result, 'GB2312')
                  //let str = Iconv.decode(Buffer.from(result, 'binary'), 'iso-8859-1')
                  let str = result.toString()
                  this.$refs.refDeployTerminal.append(`\n${str}`)
                  if (null != dealFun) {
                    dealFun(str)
                    dealFun = null
                  }
                })

                let execute = (command, callback) => {
                  dealFun = callback
                  stream.write(command)
                }

                let command = `${sslServer.after_command}\n`
                execute(command, (data) => {
                  console.log('data: ', data)
                  this.$message.success('部署操作完成')
                  setTimeout(() => {
                    this.$refs.refDeployTerminal.hide()
                  }, 3000)
                })
              })
            }
          }

          sshClient.sftp((err, sftp) => {
            if (err) {
              this.$refs.refDeployTerminal.append('SFTP::fail\n')
              console.log('fail: ', err)
              throw err
            }

            console.log('need upload files:', needFiles.upload)
            Object.keys(needFiles.upload).forEach(filename => {
              const sslPath = this._findSavePath(filename)
              console.log('替换文件 本地：', sslPath, '服务器：', needFiles.upload[filename])
              sftp.fastPut(sslPath, needFiles.upload[filename], {}, (err) => {
                if (err) {
                  this.$refs.refDeployTerminal.append('SFTP::fail\n')
                  console.log('fail: ', err)
                  throw err
                } else {
                  this.$refs.refDeployTerminal.append(`通过SFTP更新文件::${filename} -> ${needFiles.upload[filename]}\n`)
                }
              })
            })

            setTimeout(() => {
              afterFunction()
            }, 3000)
          })



        }).on('close', () => {
          this.$refs.sshTerminalRef.append('\nclosed by timeout!')
        }).on('error', (err) => {
          console.log('exception: ', err)
          this.$refs.sshTerminalRef.append(`\nerror: ${err.level}`)
        }).connect({
          port: 22,
          host: server.address,
          username: proof.username,
          password: proof.password,
          encoding: 'utf-8'
        })
      })

    },

    /**
     * 续期业务逻辑.
     * @param row {Object}
     */
    renewal(row) {
      let supplement = ''
      if (EnumPlatform.Tencent.type === this.activeIndex) {
        supplement = '同时，当前只支持DNS自动验证，即域名的DNS解析需要托管在腾讯。'
      }
      this.$confirm(`该操作为免费SSL证书续期，相当于重新申请新证书，将会新产生一条证书记录。${supplement}确定需要进行该操作吗？`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        if (EnumPlatform.Tencent.type === this.activeIndex) {
          ApiTencentSSL.freeApply(row.domain, this.getAccount(), (data) => {
            console.log('freeApply: ', data)
            const certificateId = data.CertificateId
            // 证书关联服务器复制
            StoreSSLServer.getAll({
              cloud_type: this.activeIndex,
              ssl_id: row.id
            }, (data) => {
              data.forEach((item) => {
                let newObject = Object.assign({}, item)
                delete newObject.id
                newObject.ssl_id = certificateId
                StoreSSLServer.insert(newObject)
              })
            })
            this.$message.success('续期成功')
            this.getData(true)
          }, (err) => {
            MessageHandler.error(err)
          })
        }
      })
    }
  },
  mounted() {
    MessageHandler.init(this)
    StoreCloudAccount.init(this)
    StoreSSLServer.init(this)
    StoreServer.init(this)
    StoreProof.init(this)
    this.loadAccount()
  }
}
</script>

<style scoped>

</style>